package com.seventeen.monster.redisson.autoconfigure;

import com.seventeen.monster.redisson.core.DelayedQueueHandler;
import com.seventeen.monster.redisson.support.DefaultDelayedQueueListenerContainer;
import com.seventeen.monster.redisson.annotation.DelayedQueueListener;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author seventeen
 */
@Configuration
public class ListenerContainerConfiguration implements ApplicationContextAware {
    private final static Logger log = LoggerFactory.getLogger(ListenerContainerConfiguration.class);
    private final AtomicLong counter = new AtomicLong(0);
    private final List<DefaultDelayedQueueListenerContainer> containers = new ArrayList<>();
    private RedissonClient redissonClient;
    private ConfigurableApplicationContext applicationContext;

    public ListenerContainerConfiguration(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    }

    public void registerContainer(String beanName, Object bean, DelayedQueueListener annotation) {
        Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean);
        if (!DelayedQueueHandler.class.isAssignableFrom(bean.getClass())) {
            throw new IllegalStateException(clazz + " is not instance of " + DelayedQueueHandler.class.getName());
        }
        String containerBeanName = String.format("%s_%s", DefaultDelayedQueueListenerContainer.class.getName(),
                counter.incrementAndGet());
        GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;

        genericApplicationContext.registerBean(containerBeanName, DefaultDelayedQueueListenerContainer.class,
                () -> createDelayedQueueListenerContainer(containerBeanName, bean, annotation));
        DefaultDelayedQueueListenerContainer container = genericApplicationContext.getBean(containerBeanName,
                DefaultDelayedQueueListenerContainer.class);
        containers.add(container);
        log.info("Register the delayed queue listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName);
    }

    private DefaultDelayedQueueListenerContainer createDelayedQueueListenerContainer(String name, Object bean,
                                                                                     DelayedQueueListener annotation) {
        DefaultDelayedQueueListenerContainer container = new DefaultDelayedQueueListenerContainer();
        container.setName(name);
        container.setDelayedQueueListener(annotation);
        container.setDelayedQueueHandler((DelayedQueueHandler) bean);
        container.setRedissonClient(redissonClient);
        return container;
    }

    public void startContainer() {
        for (DefaultDelayedQueueListenerContainer container : containers) {
            if (!container.isRunning()) {
                try {
                    container.start();
                } catch (Exception e) {
                    log.error("Start delay queue listener container failed. {}", container, e);
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public void stopContainer() {
        for (DefaultDelayedQueueListenerContainer container : containers) {
            if (container.isRunning()) {
                try {
                    container.stop();
                } catch (Exception e) {
                    log.error("Stop delay queue listener container failed. {}", container, e);
                }
            }
        }
    }
}
